﻿using JumpstartApp.Helpers;
using JumpstartApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JumpstartApp.ViewModels
{
    using JumpstartApp.Contracts;
    using JumpstartApp;
    using JumpstartApp.Helpers;
    using JumpstartApp.Models;

    using Windows.Foundation;
    using Windows.Graphics.Printing;
    using Windows.UI.Core;
    using Windows.UI.StartScreen;
    using Windows.UI.ViewManagement;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Printing;

    public class DetailViewModel : BindableBase
    {
        private Page page;

        private AppBar appBar;

        public DetailViewModel()
        {
            if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
                LoadData_DesignTime();
            else
            {
                this.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName.Equals("Selected") && this.Selected != null)
                    {
                        Services.Navigation.GotoDetail(this.Selected.Id);
                        this.Selected = null;
                    }
                };
            }
        }

        #region LoadData

        void LoadData_DesignTime()
        {
            this.Parent = Data.SampleData().Cast<ParentBase>().First();
            this.Child = this.Parent.Children.First();

            // show others in group, without current
            this.Children.Clear();
            foreach (var item in this.Parent.Children.Where(x => x != this.Child))
                this.Children.Add(Resize(item));
        }

        private async Task LoadDate_RunTime(string id)
        {
            this.m_episodeNotes = await EpisodeNotes.GetNotes();
            var _Data = await JumpstartApp.Data.RealData();

            var _Children = _Data.Cast<ParentBase>().SelectMany(x => x.Children);
            var _Child = _Children.FirstOrDefault(x => x.Id.Equals(id)) as ChildBase;
            this.Parent = _Data.Cast<ParentBase>().FirstOrDefault(x => x.Children.Contains(_Child));
            this.Child = _Child;

            // show others in group, without current
            this.Children.Clear();
            foreach (var item in this.Parent.Children.Where(x => x != this.Child).Where(x => !(x is AdvertRecord)))
            {
                this.Children.Add(this.Resize(item));
            }

            this.RaisePropertyChanged("Notes");
        }

        ChildBase Resize(ChildBase data)
        {
            // default size in grid
            data.ColSpan = 256;
            data.RowSpan = 144;
            return data;
        }

        #endregion

        public async Task Start(Page page, Canvas container, AppBar detailAppBar, string id)
        {
            this.page = page;
            this.printContainer = container;
            this.appBar = detailAppBar;
            this.RegisterForPrinting();
            await this.LoadDate_RunTime(id);

            this.SetupAppBarForSecondaryTiles();
        }

        public void Stop()
        {
            this.UnRegisterForPrinting();
            this.m_episodeNotes.Save();
        }

        #region Secondary Tiles

        private void SetupAppBarForSecondaryTiles()
        {
            // use the session ID for the tile ID
            ShowPinButton = !SecondaryTile.Exists(this.Child.Id);
            appBar.Opened += AppBarOnOpened;
        }

        private void AppBarOnOpened(object sender, object e)
        {
            ShowPinButton = !SecondaryTile.Exists(this.Child.Id);
        }

        private async void PinCommandExecute(Button button)
        {
            appBar.IsSticky = true;

            if (SecondaryTile.Exists(this.Child.Id))
            {
                var secondaryTile = new SecondaryTile(this.Child.Id);
                ShowPinButton = await secondaryTile.RequestDeleteForSelectionAsync(
                    GetElementRect((FrameworkElement)button),
                    Windows.UI.Popups.Placement.Above);
            }
            else
            {
                Uri logo = new Uri("ms-appx:///Assets/logo.png");
                string tileActivationArguments = this.Child.Id + " was pinned at " + DateTime.Now.ToLocalTime().ToString();

                var secondaryTile = new SecondaryTile(this.Child.Id,
                                                                this.Child.Title,
                                                                this.Child.Description,
                                                                tileActivationArguments,
                                                                TileOptions.ShowNameOnLogo,
                                                                logo);

                secondaryTile.ForegroundText = ForegroundText.Light;
                secondaryTile.SmallLogo = new Uri("ms-appx:///Assets/smalllogo.png");
                // if we wanted the large tile
                //secondaryTile.WideLogo = new Uri("ms-appx:///Assets/widelogo.png");

                ShowPinButton = !await secondaryTile.RequestCreateForSelectionAsync(
                    GetElementRect((FrameworkElement)button),
                    Windows.UI.Popups.Placement.Above);
            }

            appBar.IsSticky = false;
        }

        public static Rect GetElementRect(FrameworkElement element)
        {
            GeneralTransform buttonTransform = element.TransformToVisual(null);
            Point point = buttonTransform.TransformPoint(new Point());
            return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
        }

        #endregion

        #region Printing

        // this is what we will be printing
        PrintDocument doc = null;

        // the first page... 
        FrameworkElement page1;

        // Print container from page
        private Canvas printContainer;

        // page margins
        protected const double left = 0.075;
        protected const double top = 0.03;

        // #1
        private void RegisterForPrinting()
        {
            doc = new PrintDocument();

            // Attach handlers to relevant events
            doc.GetPreviewPage += OnGetPreviewPage;
            doc.Paginate += OnPaginate;
            doc.AddPages += OnAddPages;

            PrintManager printManager = PrintManager.GetForCurrentView();
            printManager.PrintTaskRequested += OnPrintTaskRequested;
        }

        // #2 - Provides support to show the Print UI via a button on the AppBar
        private async void PrintCommandExecute()
        {
            // Don't act when in snapped mode
            if (ApplicationView.Value != ApplicationViewState.Snapped)
            {
                await PrintManager.ShowPrintUIAsync();
            }
        }

        // #3 - User selects printer
        void OnPrintTaskRequested(PrintManager sender,
                   PrintTaskRequestedEventArgs args)
        {
            // This gets invoked as soon as the Devices pane is shown
            PrintTask task = args.Request.CreatePrintTask(
                this.Child.Title, // what you see in the spooler
                async (taskArgs) =>
                {
                    // This is invoked on a background thread when the Print
                    // button is clicked
                    var deferral = taskArgs.GetDeferral();
                    await page.Dispatcher.RunAsync(
                        CoreDispatcherPriority.Normal,
                        () =>
                        {
                            // This must run on the main thread
                            taskArgs.SetSource(doc.DocumentSource);
                            deferral.Complete();
                        });
                });
        }

        // #4 - user has selected a printer so we know paper size, etc.
        private void OnPaginate(object sender, PaginateEventArgs e)
        {
            PrintTaskOptions printingOptions = e.PrintTaskOptions;
            PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

            page1 = new DetailPrinting
            {
                DataContext = this.Child,
                Width = pageDescription.PageSize.Width,
                Height = pageDescription.PageSize.Height
            };

            // Assumes we have this on our printable page
            Grid printableArea = (Grid)page1.FindName("printableArea");

            // size our grid to the paper dimensions
            double marginWidth = Math.Max(
                pageDescription.PageSize.Width - pageDescription.ImageableRect.Width,
                pageDescription.PageSize.Width * left * 2);
            double marginHeight = Math.Max(
                pageDescription.PageSize.Height - pageDescription.ImageableRect.Height,
                pageDescription.PageSize.Height * top * 2);

            printableArea.Width = page1.Width - marginWidth;
            printableArea.Height = page1.Height - marginHeight;

            // We add the printable page to the visual tree so we can lay it out
            printContainer.Children.Add(page1);
            printContainer.InvalidateMeasure();
            printContainer.UpdateLayout();

            // in our (relatively) simple example we only have one page
            doc.SetPreviewPageCount(1, PreviewPageCountType.Intermediate);
        }

        // #5 Once print preview is displayed
        void OnGetPreviewPage(object sender, GetPreviewPageEventArgs e)
        {
            doc.SetPreviewPage(e.PageNumber, page1);
        }

        // #6 Once Print is hit - add the pages created above to the print document
        void OnAddPages(object sender, AddPagesEventArgs e)
        {
            this.doc.AddPage(page1);
            doc.AddPagesComplete();
        }

        // #7 When we leave the page, unregister from printing
        private void UnRegisterForPrinting()
        {
            // Detach handlers to relevant events
            doc.GetPreviewPage -= OnGetPreviewPage;
            doc.AddPages -= OnAddPages;

            PrintManager printManager = PrintManager.GetForCurrentView();
            printManager.PrintTaskRequested -= OnPrintTaskRequested;
        }


        #endregion

        #region Notes

        private EpisodeNotes m_episodeNotes;

        public string Notes
        {
            get
            {
                if (m_episodeNotes == null)
                {
                    return string.Empty;
                }
                return m_episodeNotes[this.Child.Id];
            }

            set
            {
                m_episodeNotes[this.Child.Id] = value;
            }
        }

        #endregion

        Models.ParentBase m_Parent = default(Models.ParentBase);
        public Models.ParentBase Parent { get { return m_Parent; } set { SetProperty(ref m_Parent, value); } }

        Models.ChildBase m_Selected = default(Models.ChildBase);
        public Models.ChildBase Selected { get { return m_Selected; } set { SetProperty(ref m_Selected, value); } }

        Models.ChildBase m_Child = default(Models.ChildBase);
        public Models.ChildBase Child { get { return m_Child; } set { SetProperty(ref m_Child, value); } }

        ObservableCollection<ChildBase> m_Children = new ObservableCollection<ChildBase>();
        public ObservableCollection<ChildBase> Children { get { return m_Children; } }

        private bool m_showPinButton;
        public bool ShowPinButton { get { return m_showPinButton; } set { SetProperty(ref m_showPinButton, value); } }

        #region Print DelegateCommand

        DelegateCommand m_PrintCommand = null;

        public DelegateCommand PrintCommand
        {
            get
            {
                if (this.m_PrintCommand != null)
                    return this.m_PrintCommand;
                this.m_PrintCommand = new DelegateCommand(PrintCommandExecute);
                return this.m_PrintCommand;
            }
        }

        #endregion

        #region Pin/UnPin DelegateCommand

        DelegateCommand<Button> m_PinCommand = null;

        public DelegateCommand<Button> PinCommand
        {
            get
            {
                if (this.m_PinCommand != null)
                    return this.m_PinCommand;
                this.m_PinCommand = new DelegateCommand<Button>(PinCommandExecute);
                return this.m_PinCommand;
            }
        }

        #endregion
    }
}
